/*******************************************************************************
 * Copyright (c) 2007, 2016 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Markus Schorn - initial API and implementation
 *******************************************************************************/ 
package org.eclipse.cdt.internal.ui.wizards.indexwizards;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;

import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.ui.CElementLabelProvider;
import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.cdt.internal.core.pdom.TeamPDOMExportOperation;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;

import org.eclipse.cdt.internal.ui.viewsupport.ListContentProvider;
 
public class TeamProjectIndexExportWizardPage extends WizardPage implements Listener {
    private static final int SIZING_TEXT_FIELD_WIDTH = 250;

    private IStructuredSelection fInitialSelection;
	private CheckboxTableViewer fProjectViewer;
    private Text fDestinationField;
    private Button fResourceSnapshotButton;

    /**
     *	Create an instance of this class
     */
    protected TeamProjectIndexExportWizardPage(String name, IStructuredSelection selection) {
        super(name);
        fInitialSelection= selection;
    }

    /**
     * Create an instance of this class.
     *
     * @param selection the selection
     */
    public TeamProjectIndexExportWizardPage(IStructuredSelection selection) {
        this("indexExportPage", selection); //$NON-NLS-1$
        setTitle(Messages.TeamProjectIndexExportWizardPage_title); 
        setDescription(Messages.TeamProjectIndexExportWizardPage_description); 
    }

    @Override
	public void createControl(Composite parent) {
        initializeDialogUnits(parent);

        Composite composite = new Composite(parent, SWT.NULL);
        composite.setLayout(new GridLayout());
        composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
                | GridData.HORIZONTAL_ALIGN_FILL));
        composite.setFont(parent.getFont());

        createResourcesGroup(composite);
        createDestinationGroup(composite);

        restoreWidgetValues(); 
        if (fInitialSelection != null) {
			setupBasedOnInitialSelections();
		}
        setupDestination();

        updateWidgetEnablements();
        setPageComplete(determinePageCompletion());
        setErrorMessage(null);	// should not initially have error message
        
        setControl(composite);
        giveFocusToDestination();
    }

	/**
     * Creates the checkbox tree and list for selecting resources.
     *
     * @param parent the parent control
     */
    private final void createResourcesGroup(Composite parent) {
        Composite resourcesGroup = new Composite(parent, SWT.NONE);
        resourcesGroup.setLayout(new GridLayout());
        resourcesGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
        resourcesGroup.setFont(parent.getFont());

        new Label(resourcesGroup, SWT.NONE).setText(Messages.TeamProjectIndexExportWizardPage_labelProjectTable);       
        Table table= new Table(resourcesGroup, SWT.CHECK | SWT.BORDER);
        table.setLayoutData(new GridData(GridData.FILL_BOTH));
		fProjectViewer= new CheckboxTableViewer(table);
		fProjectViewer.setContentProvider(new ListContentProvider());
		fProjectViewer.setLabelProvider(new CElementLabelProvider());        
        ICheckStateListener checkListener = new ICheckStateListener() {
            @Override
			public void checkStateChanged(CheckStateChangedEvent event) {
                updateWidgetEnablements();
            }
        };
        fProjectViewer.addCheckStateListener(checkListener);
        	

        // top level group
        Composite buttonComposite = new Composite(resourcesGroup, SWT.NONE);
        buttonComposite.setFont(parent.getFont());

        GridLayout layout = new GridLayout(2, true);
        layout.marginHeight= layout.marginWidth= 0;
        buttonComposite.setLayout(layout);
        buttonComposite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
                | GridData.HORIZONTAL_ALIGN_FILL));


        Button selectButton = createButton(buttonComposite,
                IDialogConstants.SELECT_ALL_ID, Messages.TeamProjectIndexExportWizardPage_selectAll, false);

        SelectionAdapter listener = new SelectionAdapter() {
            @Override
			public void widgetSelected(SelectionEvent e) {
                fProjectViewer.setAllChecked(true);
                updateWidgetEnablements();
            }
        };
        selectButton.addSelectionListener(listener);

        Button deselectButton = createButton(buttonComposite,
                IDialogConstants.DESELECT_ALL_ID, Messages.TeamProjectIndexExportWizardPage_deselectAll, false);

        listener = new SelectionAdapter() {
            @Override
			public void widgetSelected(SelectionEvent e) {
            	fProjectViewer.setAllChecked(false);
                updateWidgetEnablements();
            }
        };
        deselectButton.addSelectionListener(listener);

        initProjects();
    }

    private Button createButton(Composite parent, int id, String label,
            boolean defaultButton) {
        Button button = new Button(parent, SWT.PUSH);

        GridData buttonData = new GridData(GridData.FILL_HORIZONTAL);
        button.setLayoutData(buttonData);

        button.setData(Integer.valueOf(id));
        button.setText(label);
        button.setFont(parent.getFont());

        if (defaultButton) {
            Shell shell = parent.getShell();
            if (shell != null) {
                shell.setDefaultButton(button);
            }
            button.setFocus();
        }
        button.setFont(parent.getFont());
        setButtonLayoutData(button);
        return button;
    }

    private void initProjects() {
        ArrayList<ICProject> input = new ArrayList<ICProject>();
        ICProject[] projects;
		try {
			projects = CoreModel.getDefault().getCModel().getCProjects();
	        for (ICProject project : projects) {
	            if (project.getProject().isOpen()) {
					input.add(project);
				}
	        }
		} catch (CModelException e) {
			CUIPlugin.log(e);
		}
		fProjectViewer.setInput(input);
	}

    private void setupBasedOnInitialSelections() {
    	HashSet<String> names= new HashSet<String>();
        Iterator<?> it = fInitialSelection.iterator();
        while (it.hasNext()) {
            IProject project = (IProject) it.next();
            names.add(project.getName());
        }
        
        Collection<?> prjs= (Collection<?>) fProjectViewer.getInput();
        for (Object element : prjs) {
			ICProject prj = (ICProject) element;
			if (names.contains(prj.getElementName())) {
	            fProjectViewer.setChecked(prj, true);
			}
		}
    }
    
    private void setupDestination() {
    	String dest;
    	ICProject[] prjs= getCheckedElements();
    	if (prjs.length > 0) {
    		dest= IndexerPreferences.getIndexImportLocation(prjs[0].getProject());
    	}
    	else {
    		dest= IndexerPreferences.getIndexImportLocation(null);
    	}
    	fDestinationField.setText(dest);
    }
    	
    
    private void createDestinationGroup(Composite parent) {
    	GridData gd;
        Font font = parent.getFont();
        // destination specification group
        Composite destinationSelectionGroup = new Composite(parent, SWT.NONE);
        destinationSelectionGroup.setLayout(new GridLayout(2, false));
        destinationSelectionGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL));
        destinationSelectionGroup.setFont(font);

        Label destinationLabel = new Label(destinationSelectionGroup, SWT.NONE);
        destinationLabel.setText(Messages.TeamProjectIndexExportWizardPage_destinationLabel); 
        destinationLabel.setFont(font);
        destinationLabel.setLayoutData(gd= new GridData());
        gd.horizontalSpan= 2;
        
        // destination name entry field
        fDestinationField = new Text(destinationSelectionGroup, SWT.BORDER);
        fDestinationField.addListener(SWT.Modify, this);
        fDestinationField.addListener(SWT.Selection, this);
        fDestinationField.setFont(font);
        fDestinationField.setLayoutData(gd= new GridData());
        gd.grabExcessHorizontalSpace= true;
        gd.horizontalAlignment= GridData.FILL;
        gd.widthHint = SIZING_TEXT_FIELD_WIDTH;

        Button button= createButton(destinationSelectionGroup, IDialogConstants.CLIENT_ID, Messages.TeamProjectIndexExportWizardPage_variableButton, false);
        SelectionAdapter listener = new SelectionAdapter() {
            @Override
			public void widgetSelected(SelectionEvent e) {
                onInsertVariable();
            }
        };
        button.addSelectionListener(listener);

        // resource snapshot destination group
        Composite resourceSnapshotDestinationGroup = new Composite(parent, SWT.NONE);
        resourceSnapshotDestinationGroup.setLayout(new GridLayout(1, false));
        resourceSnapshotDestinationGroup.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL));
        resourceSnapshotDestinationGroup.setFont(font);
        
        fResourceSnapshotButton = new Button(resourceSnapshotDestinationGroup, SWT.CHECK);
        fResourceSnapshotButton.setText(Messages.TeamProjectIndexExportWizardPage_resourceSnapshotButton);
        fResourceSnapshotButton.setFont(font);
        fResourceSnapshotButton.setLayoutData(gd= new GridData());
        gd.grabExcessHorizontalSpace= true;
        gd.horizontalAlignment= GridData.FILL;
    }

	protected void onInsertVariable() {
		StringVariableSelectionDialog dlg= new StringVariableSelectionDialog(getShell());
		if (dlg.open() == Window.OK) {
			String var= dlg.getVariableExpression();
			fDestinationField.insert(var);
		}
	}

	public boolean finish() {
        ICProject[] projectsToExport= getCheckedElements();

        // about to invoke the operation so save our state
        saveWidgetValues();
        return executeExportOperation(projectsToExport);
    }

	private void saveWidgetValues() {
	}

	private void restoreWidgetValues() {
	}


	private ICProject[] getCheckedElements() {
		Object[] obj= fProjectViewer.getCheckedElements();
		ICProject[] prjs= new ICProject[obj.length];
		System.arraycopy(obj, 0, prjs, 0, obj.length);
		return prjs;
	}

    private boolean executeExportOperation(final ICProject[] projects) {
    	final String dest= getDestinationValue();
    	final MultiStatus status= new MultiStatus(CUIPlugin.PLUGIN_ID, 
    			0, Messages.TeamProjectIndexExportWizardPage_errorExporting, null); 
    	final boolean exportResourceSnapshot = fResourceSnapshotButton.getSelection();

    	IRunnableWithProgress op= new IRunnableWithProgress() {
			@Override
			public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
				monitor.beginTask("", projects.length); //$NON-NLS-1$
				for (ICProject project : projects) {
					TeamPDOMExportOperation op= new TeamPDOMExportOperation(project);
					op.setTargetLocation(dest);
					if (exportResourceSnapshot) {
						op.setOptions(TeamPDOMExportOperation.EXPORT_OPTION_RESOURCE_SNAPSHOT);
					}
					try {
						op.run(new SubProgressMonitor(monitor, 1));
					} catch (CoreException e) {
						status.merge(e.getStatus());
					}
				}
			}
    	};
        try {
            getContainer().run(true, true, op);
        } catch (InterruptedException e) {
            return false;
        } catch (InvocationTargetException e) {
            CUIPlugin.log(Messages.TeamProjectIndexExportWizardPage_errorExporting,
            		e.getTargetException());
            displayErrorDialog(e.getTargetException());
            return false;
        }

        if (!status.isOK()) {
        	CUIPlugin.log(status);
            ErrorDialog.openError(getContainer().getShell(),
                    getErrorDialogTitle(),
                    null, // no special message
                    status);
            return false;
        }

        return true;
    }

    private String getDestinationValue() {
        return fDestinationField.getText().trim();
    }

    private void giveFocusToDestination() {
    	fDestinationField.setFocus();
    }

    /**
     *	Answer a boolean indicating whether the receivers destination specification
     *	widgets currently all contain valid values.
     */
	protected boolean validateDestinationGroup() {
        String destinationValue = getDestinationValue();
        if (destinationValue.length() == 0) {
            setMessage(Messages.TeamProjectIndexExportWizardPage_destinationMessage); 
            return false;
        }

        setErrorMessage(null);
        return true;
    }

	protected boolean validateSourceGroup() {
    	// there must be some resources selected for Export
    	boolean isValid = true;
        Object[] projectsToExport = getCheckedElements();
    	if (projectsToExport.length == 0){
    		setErrorMessage(Messages.TeamProjectIndexExportWizardPage_noProjectError); 
            isValid =  false;
    	} else {
			setErrorMessage(null);
		}
		return isValid;
	}

	protected void updateWidgetEnablements() {
        boolean pageComplete = determinePageCompletion();
        setPageComplete(pageComplete);
        if (pageComplete) {
			setMessage(null);
		}
    }
    

	@Override
	public void handleEvent(Event event) {
		updateWidgetEnablements();
	}
	
	protected String getErrorDialogTitle() {
        return Messages.TeamProjectIndexExportWizardPage_errorDlgTitle; 
    }

    /**
     * Returns whether this page is complete. This determination is made based upon
     * the current contents of this page's controls.  Subclasses wishing to include
     * their controls in this determination should override the hook methods 
     * <code>validateSourceGroup</code> and/or <code>validateOptionsGroup</code>.
     *
     * @return <code>true</code> if this page is complete, and <code>false</code> if
     *   incomplete
     * @see #validateSourceGroup
     * @see #validateDestinationGroup
     */
    private boolean determinePageCompletion() {
        boolean complete = validateSourceGroup() && validateDestinationGroup();

        // Avoid draw flicker by not clearing the error
        // message unless all is valid.
        if (complete) {
			setErrorMessage(null);
		}

        return complete;
    }

    /**
     * Determine if the page is complete and update the page appropriately. 
     */
    protected void updatePageCompletion() {
        boolean pageComplete = determinePageCompletion();
        setPageComplete(pageComplete);
        if (pageComplete) {
            setErrorMessage(null);
        }
    }

    /**
     * Display an error dialog with the specified message.
     *
     * @param message the error message
     */
    private void displayErrorDialog(String message) {
        MessageDialog.open(MessageDialog.ERROR, getContainer().getShell(),
                getErrorDialogTitle(), message, SWT.SHEET);
    }

    /**
     * Display an error dislog with the information from the
     * supplied exception.
     * @param exception Throwable
     */
    private void displayErrorDialog(Throwable exception) {
        String message = exception.getMessage();
        //Some system exceptions have no message
        if (message == null) {
			message = NLS.bind(Messages.TeamProjectIndexExportWizardPage_errorInOperation, exception);
		}
        displayErrorDialog(message);
    }
}
